package cn.song.docflow.controller;

import cn.song.docflow.common.CustomException;
import cn.song.docflow.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static cn.song.docflow.controller.DocumentController.extractFileName;

/**
 * 文件控制器
 *
 * @author SongRenShuo
 * @date 2023/05/20
 */
@Slf4j
@RestController
@RequestMapping("/api/file")
public class FileController {

    /**
     * 文件上传目录
     */
    @Value("${file.upload-dir:uploads/}")
    private String uploadDir;
    /**
     * 基础URL
     */
    @Value("${baseUrl:http://localhost:58080}")
    private String baseUrl;

    /**
     * 允许的文件类型
     */
    private static final String[] ALLOWED_TEMPLATE_EXTENSIONS = {"docx"};
    private static final String[] ALLOWED_IMAGE_EXTENSIONS = {"jpg", "jpeg", "png"};

    /**
     * 上传模板文件
     *
     * @param file 文件
     * @return 返回对象
     */
    @PostMapping("/upload-template")
    public R<Map<String, String>> uploadTemplate(@RequestParam("file") MultipartFile file) {
        return uploadFile(file, "templates", ALLOWED_TEMPLATE_EXTENSIONS);
    }

    /**
     * 上传图片文件
     *
     * @param file 文件
     * @return 返回对象
     */
    @PostMapping("/upload-image")
    public R<Map<String, String>> uploadImage(@RequestParam("file") MultipartFile file) {
        return uploadFile(file, "images", ALLOWED_IMAGE_EXTENSIONS);
    }

    /**
     * 上传文件
     *
     * @param file       文件
     * @param subDir     子目录
     * @param extensions 允许的扩展名
     * @return 返回对象
     */
    private R<Map<String, String>> uploadFile(MultipartFile file, String subDir, String[] extensions) {
        try {
            if (file.isEmpty()) {
                return R.failed(400,"文件为空", null);
            }

            // 检查文件类型
            String originalFilename = file.getOriginalFilename();
            String extension = getFileExtension(originalFilename);
            if (!isAllowedExtension(extension, extensions)) {
                return R.failed(400,"不支持的文件类型："+extension, null);
            }

            // 创建目录 - 确保基础上传目录是绝对路径并规范化
            Path baseDir = Paths.get(uploadDir).toAbsolutePath().normalize();
            Path targetPath = baseDir.resolve(subDir).normalize();

            // 确保目标路径在基础目录下（防止路径遍历）
            if (!targetPath.startsWith(baseDir)) {
                throw new IOException("非法的目录访问");
            }

            if (!Files.exists(targetPath)) {
                Files.createDirectories(targetPath);
            }

            // 生成安全的文件名
            String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
            String uuid = UUID.randomUUID().toString().substring(0, 8);
            String newFilename = timestamp + "_" + uuid + "." + extension;

            // 构造最终文件路径并验证
            Path finalFilePath = targetPath.resolve(newFilename).normalize();

            // 双重检查：确保最终路径仍在允许的目录范围内
            if (!finalFilePath.startsWith(targetPath)) {
                throw new IOException("非法的文件路径");
            }

            // 保存文件
            Files.write(finalFilePath, file.getBytes());

            // 返回结果
            Map<String, String> result = new HashMap<>();
            result.put("fileName", newFilename);
            // 完整URL
            result.put("fileUrl", getRelativeFilePath(subDir, newFilename));
            // 相对路径
            result.put("filePath", "/" + subDir + "/" + newFilename);
            result.put("originalFileName", originalFilename);

            log.info("文件上传成功: {}", finalFilePath);
            return R.success( "文件上传成功",result);
        } catch (IOException e) {
            log.error("文件上传失败: {}", e.getMessage());
            return R.failed("文件上传失败: " + e.getMessage());
        }
    }


    /**
     * 获取文件扩展名
     *
     * @param filename 文件名
     * @return 扩展名
     */
    private String getFileExtension(String filename) {
        if (filename == null || filename.trim().isEmpty()) {
            throw new CustomException("文件名不能为空");
        }

        // 安全地提取文件名，避免路径遍历
        String safeFilename = extractSafeFilename(filename);

        int lastDotIndex = safeFilename.lastIndexOf(".");
        if (lastDotIndex == -1 || lastDotIndex == safeFilename.length() - 1) {
            throw new CustomException("无效的文件名，缺少扩展名");
        }

        return safeFilename.substring(lastDotIndex + 1).toLowerCase();
    }

    /**
     * 安全地从文件名中提取纯文件名部分，防止路径遍历攻击
     * 从根本上避免路径遍历问题，不使用File对象
     */
    private String extractSafeFilename(String filename) {
        if (filename == null || filename.trim().isEmpty()) {
            throw new CustomException("文件名不能为空");
        }

        // 移除所有可能导致路径遍历的字符和序列
        String safeFilename = filename.replaceAll("[/\\\\\u0000\\n\\r\\t]", "");

        // 检查是否包含路径遍历序列
        if (safeFilename.contains("..")) {
            throw new CustomException("文件名包含非法路径序列");
        }

        // 安全地提取文件名（移除任何路径部分）
        int lastSeparatorIndex = filename.lastIndexOf('/');
        int lastAltSeparatorIndex = filename.lastIndexOf('\\');

        // 取两种分隔符中最后出现的位置
        int lastIndex = Math.max(lastSeparatorIndex, lastAltSeparatorIndex);

        // 如果有分隔符，则只保留文件名部分
        if (lastIndex != -1) {
            safeFilename = filename.substring(lastIndex + 1);
        }

        // 再次检查安全文件名
        if (safeFilename.contains("..")) {
            throw new CustomException("文件名包含非法路径序列");
        }

        return safeFilename;
    }

    /**
     * 检查是否为允许的扩展名
     *
     * @param extension  扩展名
     * @param extensions 允许的扩展名数组
     * @return 是否允许
     */
    private boolean isAllowedExtension(String extension, String[] extensions) {
        return Arrays.asList(extensions).contains(extension.toLowerCase());
    }

    /**
     * 将绝对路径转换为可访问的完整URL
     *
     * @param subPath 子路径
     * @param absolutePath 绝对路径
     * @return 可访问的文件URL
     */
    private String getRelativeFilePath(String subPath, String absolutePath) {
        try {
            // 安全地提取文件名 - 从根本上避免路径遍历问题
            String fileName = extractFileName(absolutePath);

            // 返回以http开头的完整URL
            return baseUrl + "/" +
                    subPath + "/" +
                    URLEncoder.encode(fileName, "UTF-8");
        } catch (Exception e) {
            log.error("生成文件URL失败: {}", e.getMessage(), e);
            throw new RuntimeException("生成文件URL失败", e);
        }
    }
}
